#include <iostream>
#include <cstdlib>
#include <pthread.h>
#include <unistd.h>
#include <queue>

#define MAX_QUEUE 5
template <class T>
class BlockQueue
{
  private:
    int _capacity;
    std::queue<T> _queue;
    pthread_mutex_t _mutex;
    pthread_cond_t _cond_pro;
    pthread_cond_t _cond_con;
  public:
    BlockQueue(int cap= MAX_QUEUE):_capacity(cap)
    {
      pthread_mutex_init(&_mutex,NULL );
      pthread_cond_init(&_cond_con,NULL );
      pthread_cond_init(&_cond_pro,NULL);

    }
    ~BlockQueue()
    {

      pthread_mutex_destroy(&_mutex );  
      pthread_cond_destroy(&_cond_con );
      pthread_cond_destroy(&_cond_pro);
    }
    bool Push(const T &data)
    {
      pthread_mutex_lock(&_mutex);
      while(_queue.size()==_capacity)
      {
        pthread_cond_wait(&_cond_pro,&_mutex);
      }
      _queue.push(data);
      pthread_mutex_unlock(&_mutex);
      pthread_cond_signal(&_cond_con);
    }
    bool Pop(T *data)
    {

      pthread_mutex_lock(&_mutex);
      while(_queue.empty()==true)
      {
        pthread_cond_wait(&_cond_con,&_mutex);
      }
     *data=_queue.front();
     _queue.pop();
      pthread_mutex_unlock(&_mutex);
      pthread_cond_signal(&_cond_pro);
    }
};

void *consumer(void *arg)
{
  BlockQueue<int>  *p=(BlockQueue<int>*)arg;
  while(1)
  {
    int data;
    p->Pop(&data);
    printf("consumer get data: %d\n",data);
  }
  return NULL;
}
void *productor(void *arg)
{

  BlockQueue<int>  *p=(BlockQueue<int>*)arg;
  int data=0;
  while(1)
  {
    p->Push(data);
    printf("productor put data:%d\n",data++);

  }
  return NULL;
}

int main()
{
  int ret,i,j;
  pthread_t ctid[4],ptid[4];

  BlockQueue<int> q;
  for(i=0;i<4;i++)
  {
    ret=pthread_create(&ctid[i],NULL,consumer,(void*)&q);
    if(ret!=0)
    {
      std::cout<<"thread create error\n";
      return -1;
    }
  
    ret=pthread_create(&ptid[i],NULL,productor,(void*)&q);
    if(ret!=0)
    {
      std::cout<<"thread create error\n";
      return -1;
    }
  }

  for(j=0;j<4;j++)
  {
    pthread_join(ctid[i],NULL);
    pthread_join(ptid[i],NULL);
  }
  return 0;
}
